﻿using System;
using System.Runtime.InteropServices;
using OpenCvSharp;
using System.Threading;
/**
* 多线程示例 (sdk支持多实例多线程，最大支持3个线程，
* 若需要使用多线程，可通过示例多方式，多一个线程则需要多初始化1次sdk。不同的线程接口如第二个线程，
* 方法名后面加2，第三个线程，方法名加3，sdk不支持同样的接口在多个线程里面调用，只支持不同线程中调。
* 即都调人脸检测到接口，则需要不同线程中调如第一个调detect方法，第二个线程调detect2方法。第三个线程调detect3方法。
* 其他接口以此类推
* 
*/
namespace testface
{
    class MultiThread
    {
        // sdk初始化(多实例多线程)
        [DllImport("BaiduFaceApi.dll", EntryPoint = "sdk_init2", CharSet = CharSet.Ansi
            , CallingConvention = CallingConvention.Cdecl)]
        public static extern int sdk_init2(string model_path);

        // sdk销毁(多实例多线程)
        [DllImport("BaiduFaceApi.dll", EntryPoint = "sdk_destroy2", CharSet = CharSet.Ansi
            , CallingConvention = CallingConvention.Cdecl)]
        public static extern void sdk_destroy2();

        // 提取人脸特征值
        [DllImport("BaiduFaceApi.dll", EntryPoint = "face_feature2", CharSet = CharSet.Ansi
            , CallingConvention = CallingConvention.Cdecl)]
        // 返回num为特征值的人数,type为0表示提取生活照的特征值，1表示nir的特征值
        public static extern int face_feature2(IntPtr feature, IntPtr box, IntPtr mat, int type);

        // sdk初始化(多实例多线程)
        [DllImport("BaiduFaceApi.dll", EntryPoint = "sdk_init3", CharSet = CharSet.Ansi
            , CallingConvention = CallingConvention.Cdecl)]
        private static extern int sdk_init3(string model_path);

        // 单目RGB静默活体检测
        [DllImport("BaiduFaceApi.dll", EntryPoint = "rgb_liveness3", CharSet = CharSet.Ansi
           , CallingConvention = CallingConvention.Cdecl)]
        public static extern int rgb_liveness3(IntPtr ptr_boxinfo, IntPtr ptr_score, IntPtr mat);

        // sdk销毁(多实例多线程)
        [DllImport("BaiduFaceApi.dll", EntryPoint = "sdk_destroy3", CharSet = CharSet.Ansi
            , CallingConvention = CallingConvention.Cdecl)]
        private static extern void sdk_destroy3();

        class ThreadA
        {
            // 人脸检测线程
            public void a_run()
            {
                // opencv mat并不是线程安全的，若多个线程中使用的是同一个图片的imread或操作，请把mat作为
                // 多个线程中的共享变量
                string img_rgb = "../images/1.jpg";
                Mat mat = Cv2.ImRead(img_rgb);
                for (int i = 0; i < 10000; i++)
                {
                    Console.WriteLine("a_run:{0}", i);
                    // 线程跑人脸检测
                    int max_detect_num = 50; // 设置最多检测跟踪人数（多人脸检测），默认为1，最多可设为50
                                             // type 0： 表示rgb 人脸检测 1：表示nir人脸检测
                    int type = 0;
                    BDFaceBBox[] info = new BDFaceBBox[max_detect_num];

                    int sizeTrack = Marshal.SizeOf(typeof(BDFaceBBox));
                    IntPtr ptT = Marshal.AllocHGlobal(sizeTrack * max_detect_num);                  
                    Console.WriteLine("before detect");
                    // 人脸检测
                    int faceNum = FaceDetect.detect(ptT, mat.CvPtr, type);
                    Console.WriteLine("faceNum is:" + faceNum);
                    // 因为需预分配内存，所以返回的人脸数若大于预先分配的内存数，则仅仅显示预分配的人脸数
                    if (faceNum > max_detect_num)
                    {
                        faceNum = max_detect_num;
                    }
                    for (int index = 0; index < faceNum; index++)
                    {

                        IntPtr ptr = new IntPtr();
                        if (8 == IntPtr.Size)
                        {
                            ptr = (IntPtr)(ptT.ToInt64() + sizeTrack * index);
                        }
                        else if (4 == IntPtr.Size)
                        {
                            ptr = (IntPtr)(ptT.ToInt32() + sizeTrack * index);
                        }

                        info[index] = (BDFaceBBox)Marshal.PtrToStructure(ptr, typeof(BDFaceBBox));

                        // 索引值
                        Console.WriteLine("detect index is:{0}", info[index].index);
                        // 置信度
                        Console.WriteLine("detect score is:{0}", info[index].score);                       
                        // 人脸宽度
                        Console.WriteLine("detect mWidth is:{0}", info[index].width);
                        // 中心点X,Y坐标
                        Console.WriteLine("detect mCenter_x is:{0}", info[index].center_x);
                        Console.WriteLine("detect mCenter_y is:{0}", info[index].center_y);


                    }
                    Marshal.FreeHGlobal(ptT);
                }
                mat.Release(); //用完释放，防止内存泄露
            }
        }

        class ThreadB
        {
            // 人脸特征值线程
            public void b_run()
            {
               
                // opencv mat并不是线程安全的，若多个线程中使用的是同一个图片的imread或操作，请把mat作为
                // 多个线程中的共享变量
                string img_rgb = "../images/2.jpg";
                Mat mat = Cv2.ImRead(img_rgb);
                for (int j = 0; j < 10000; j++)
                {
                    
                    Console.WriteLine("b_run:{0}", j);
                    // 特征值的长度，128个float值
                    int dim_count = 128;

                    // 假设提取的人数，需要比实际的人数多，因为需要提取分配内存
                    int faceNum = 5;
                    BDFaceFeature[] feaList = new BDFaceFeature[faceNum];
                    for (int i = 0; i < faceNum; i++)
                    {
                        feaList[i].data = new float[dim_count];
                        feaList[i].size = 0;
                    }                  
                    int sizeFeature = Marshal.SizeOf(typeof(BDFaceFeature));
                    IntPtr ptFea = Marshal.AllocHGlobal(sizeFeature * faceNum);

                    // 构造返回的人脸框数据
                    BDFaceBBox[] info = new BDFaceBBox[faceNum];
                    int sizeBox = Marshal.SizeOf(typeof(BDFaceBBox));
                    IntPtr ptBox = Marshal.AllocHGlobal(sizeBox * faceNum);
                    // 返回num为特征值的人数,type为0表示提取生活照的特征值，1表示证件照的特征值，2表示nir的特征值
                    int type = 0;
                    // 调用线程2的特征值提取方法
                    int num = face_feature2(ptFea, ptBox, mat.CvPtr, type);
                 
                    if (num <= 0)
                    {
                        continue ;
                    }
                    Console.WriteLine("face num is:{0}", num);
                    // 请确保faceNum大于num, faceNum为c#期望的检测人数，需要预先分配内存，否则无法获取
                    if (num > faceNum)
                    {
                        num = faceNum;
                    }
                    for (int index = 0; index < num; index++)
                    {
                        IntPtr ptrF = new IntPtr();
                        IntPtr ptrB = new IntPtr();
                        if (8 == IntPtr.Size)
                        {
                            ptrF = (IntPtr)(ptFea.ToInt64() + sizeFeature * index);
                            ptrB = (IntPtr)(ptBox.ToInt64() + sizeBox * index);
                        }
                        else if (4 == IntPtr.Size)
                        {
                            ptrF = (IntPtr)(ptFea.ToInt32() + sizeFeature * index);
                            ptrB = (IntPtr)(ptBox.ToInt32() + sizeBox * index);
                        }
                        feaList[index] = (BDFaceFeature)Marshal.PtrToStructure(ptrF, typeof(BDFaceFeature));
                        Console.WriteLine("feaList[index].size is:{0}", feaList[index].size);


                        for (int k = 0; k < feaList[index].size; k++)
                        {
                            Console.WriteLine("feature is {0}:", feaList[index].data[k]);
                        }

                        info[index] = (BDFaceBBox)Marshal.PtrToStructure(ptrB, typeof(BDFaceBBox));

                        // 索引值
                        Console.WriteLine("detect score is:{0}", info[index].index);
                        // 置信度
                        Console.WriteLine("detect score is:{0}", info[index].score);                     
                        // 人脸宽度
                        Console.WriteLine("detect mWidth is:{0}", info[index].width);
                        // 中心点X,Y坐标
                        Console.WriteLine("detect mCenter_x is:{0}", info[index].center_x);
                        Console.WriteLine("detect mCenter_y is:{0}", info[index].center_y);
                    }
                   
                }
                mat.Release(); //用完释放，防止内存泄露
            }
        }

       
        // 多线程示例
        public void test_multi_thread()
        {
            // 线程2的初始化
            sdk_init2(null);
            // 线程3的初始化
            sdk_init3(null);

            ThreadA ta = new ThreadA();
            Thread t1 = new Thread(new ThreadStart(ta.a_run));

            ThreadB tb = new ThreadB();
            Thread t2 = new Thread(new ThreadStart(tb.b_run));

          

            t1.Start();
            t2.Start();
         
            // 防止走到函数结束删除了人脸指针实例
            Console.ReadKey();
            sdk_destroy2();
            sdk_destroy3();
        }
            
    }
}
